import { Injectable, Logger } from '@nestjs/common';
import { v2 as cloudinary } from 'cloudinary';
import { CloudinaryOptions } from '../interfaces/cloudinary-options.interface';
import * as fs from 'fs';
import * as path from 'path';

@Injectable()
export class CloudinaryService {
  private readonly logger = new Logger(CloudinaryService.name);

  constructor() {}

  /**
   * 初始化 Cloudinary 配置
   * @param options Cloudinary 配置选项
   */
  initializeCloudinary(options: CloudinaryOptions): void {
    cloudinary.config({
      cloud_name: options.cloud_name,
      api_key: options.api_key,
      api_secret: options.api_secret,
    });
  }

  /**
   * 上传图片到 Cloudinary
   * @param filePath 图片路径或 URL
   * @param options 上传选项
   * @returns 上传结果
   */
  async uploadImage(
    filePath: string,
    options?: {
      public_id?: string;
      folder?: string;
      resource_type?: 'image' | 'video' | 'raw' | 'auto';
      [key: string]: any;
    },
  ): Promise<any> {
    try {
      // 检查是否是 URL
      if (filePath.startsWith('http://') || filePath.startsWith('https://')) {
        // 如果是 URL，直接上传
        this.logger.log(`上传远程图片: ${filePath}`);
        return await cloudinary.uploader.upload(filePath, options);
      } else {
        // 如果是本地文件路径，检查文件是否存在
        if (!fs.existsSync(filePath)) {
          throw new Error(`文件不存在: ${filePath}`);
        }

        // 确保文件是可读的
        try {
          fs.accessSync(filePath, fs.constants.R_OK);
        } catch {
          throw new Error(`文件无法读取 (权限问题): ${filePath}`);
        }

        this.logger.log(`上传本地图片: ${filePath}`);
        // 使用绝对路径确保 Cloudinary 可以找到文件
        const absolutePath = path.resolve(filePath);
        return await cloudinary.uploader.upload(absolutePath, options);
      }
    } catch (error) {
      this.logger.error(`上传图片失败: ${filePath}`, error);
      throw error;
    }
  }

  /**
   * 获取优化的图片 URL
   * @param publicId 图片的 public_id
   * @param options 转换选项
   * @returns 优化后的图片 URL
   */
  getOptimizedUrl(
    publicId: string,
    options?: {
      fetch_format?: string;
      quality?: string;
      [key: string]: any;
    },
  ): string {
    const defaultOptions = {
      fetch_format: 'auto',
      quality: 'auto',
    };

    return cloudinary.url(publicId, { ...defaultOptions, ...options });
  }

  /**
   * 获取转换后的图片 URL
   * @param publicId 图片的 public_id
   * @param options 转换选项
   * @returns 转换后的图片 URL
   */
  getTransformedUrl(
    publicId: string,
    options: {
      width?: number;
      height?: number;
      crop?: string;
      gravity?: string;
      [key: string]: any;
    },
  ): string {
    return cloudinary.url(publicId, options);
  }

  /**
   * 删除 Cloudinary 上的资源
   * @param publicId 要删除的资源的 public_id
   * @param options 删除选项
   * @returns 删除结果
   */
  async deleteResource(
    publicId: string,
    options?: {
      resource_type?: 'image' | 'video' | 'raw' | 'auto';
      type?: string;
      [key: string]: any;
    },
  ): Promise<any> {
    return await cloudinary.uploader.destroy(publicId, options);
  }

  /**
   * 获取资源详情
   * @param publicId 资源的 public_id
   * @param options 选项
   * @returns 资源详情
   */
  async getResourceDetails(
    publicId: string,
    options?: {
      resource_type?: 'image' | 'video' | 'raw' | 'auto';
      [key: string]: any;
    },
  ): Promise<any> {
    return await cloudinary.api.resource(publicId, options);
  }

  /**
   * 上传文件夹中的所有图片到 Cloudinary
   * @param folderPath 本地文件夹路径
   * @param targetFolder Cloudinary 目标文件夹
   * @param options 上传选项
   * @returns 上传结果数组
   */
  async uploadFolder(
    folderPath: string,
    targetFolder?: string,
    options?: {
      resource_type?: 'image' | 'video' | 'raw' | 'auto';
      [key: string]: any;
    },
  ): Promise<{
    success: { path: string; result: any }[];
    failed: { path: string; error: string }[];
  }> {
    // 检查文件夹是否存在
    if (!fs.existsSync(folderPath) || !fs.statSync(folderPath).isDirectory()) {
      throw new Error(`文件夹不存在或路径不是一个文件夹: ${folderPath}`);
    }

    const results = {
      success: [] as { path: string; result: any }[],
      failed: [] as { path: string; error: string }[],
    };

    // 递归处理文件夹
    await this.processDirectory(folderPath, targetFolder, options, results, '');

    return results;
  }

  /**
   * 递归处理目录中的文件和子目录
   * @param currentPath 当前处理的本地路径
   * @param targetFolder Cloudinary 基础目标文件夹
   * @param options 上传选项
   * @param results 结果收集对象
   * @param relativePath 相对于根目录的路径
   */
  private async processDirectory(
    currentPath: string,
    targetFolder: string | undefined,
    options: any,
    results: {
      success: { path: string; result: any }[];
      failed: { path: string; error: string }[];
    },
    relativePath: string,
  ): Promise<void> {
    const items = fs.readdirSync(currentPath);

    for (const item of items) {
      const itemPath = path.join(currentPath, item);
      const itemStat = fs.statSync(itemPath);
      const itemRelativePath = path.join(relativePath, item);

      if (itemStat.isDirectory()) {
        // 递归处理子目录
        await this.processDirectory(
          itemPath,
          targetFolder,
          options,
          results,
          itemRelativePath,
        );
      } else if (this.isImageFile(item)) {
        // 处理图片文件
        try {
          // 构建在 Cloudinary 中的路径
          let cloudinaryPath = itemRelativePath.replace(/\\/g, '/');
          // 移除文件扩展名
          cloudinaryPath = cloudinaryPath.replace(/\.[^.]+$/, '');

          // 如果有目标文件夹，将其添加到路径前面
          const fullPath = targetFolder
            ? `${targetFolder}/${cloudinaryPath}`
            : cloudinaryPath;

          // 设置上传选项，保持原始文件名结构
          const uploadOptions = {
            ...options,
            public_id: fullPath,
            use_filename: true,
            unique_filename: false,
          };

          const result = await this.uploadImage(itemPath, uploadOptions);
          results.success.push({ path: itemPath, result });
          this.logger.log(`成功上传: ${itemPath} -> ${result.public_id}`);
        } catch (error) {
          results.failed.push({
            path: itemPath,
            error: error instanceof Error ? error.message : String(error),
          });
          this.logger.error(`上传失败: ${itemPath}`, error);
        }
      }
    }
  }

  /**
   * 检查文件是否为图片
   * @param filename 文件名
   * @returns 是否为图片
   */
  private isImageFile(filename: string): boolean {
    const imageExtensions = [
      '.jpg',
      '.jpeg',
      '.png',
      '.gif',
      '.bmp',
      '.webp',
      '.svg',
      '.tiff',
    ];
    const ext = path.extname(filename).toLowerCase();
    return imageExtensions.includes(ext);
  }

  /**
   * 从 Markdown 文本中提取所有图片URL
   * @param content Markdown 文本内容
   * @returns 图片URL数组
   */
  extractImagesFromText(content: string): string[] {
    if (!content) {
      return [];
    }

    const images: string[] = [];

    try {
      // 匹配Markdown格式图片 ![alt](url)
      const markdownRegex = /!\[.*?\]\(([^\s)]+)\)/g;
      let markdownMatch;
      while ((markdownMatch = markdownRegex.exec(content)) !== null) {
        if (markdownMatch[1] && !images.includes(markdownMatch[1])) {
          images.push(markdownMatch[1]);
        }
      }

      this.logger.log(`从 Markdown 文本中提取到 ${images.length} 张图片`);
      return images;
    } catch (error) {
      this.logger.error('提取图片URL时出错', error);
      return [];
    }
  }

  /**
   * 从 Markdown 文件中提取所有图片URL
   * @param filePath Markdown 文件路径
   * @returns 图片URL数组
   */
  extractImagesFromFile(filePath: string): string[] {
    try {
      this.logger.log(`开始处理文件: ${filePath}`);

      // 使用 path.normalize 处理路径，这是正确处理 Windows 路径的方式
      const normalizedPath = path.normalize(filePath);
      this.logger.log(`标准化路径后: ${normalizedPath}`);

      // 检查文件是否存在
      if (!fs.existsSync(normalizedPath)) {
        this.logger.error(`文件不存在: ${normalizedPath}`);
        throw new Error(`文件不存在: ${normalizedPath}`);
      }

      // 确保文件是可读的
      try {
        fs.accessSync(normalizedPath, fs.constants.R_OK);
      } catch (err) {
        this.logger.error(`文件访问错误: ${err.message}`);
        throw new Error(`文件无法读取 (权限问题): ${normalizedPath}`);
      }

      // 检查文件扩展名
      const ext = path.extname(normalizedPath).toLowerCase();
      if (ext !== '.md') {
        this.logger.warn(`文件不是 Markdown 格式: ${normalizedPath}`);
      }

      // 读取文件内容
      const content = fs.readFileSync(normalizedPath, 'utf8');
      this.logger.log(
        `成功读取 Markdown 文件: ${normalizedPath}, 内容长度: ${content.length}`,
      );

      // 使用现有的方法从内容中提取图片
      return this.extractImagesFromText(content);
    } catch (error) {
      this.logger.error(`从文件提取图片失败: ${filePath}`, error);
      throw error;
    }
  }

  /**
   * 处理 Markdown 文件，上传图片并替换链接
   * @param filePath Markdown 文件路径
   * @param uploadImages 是否上传图片
   * @param generateNewFile 是否生成新文件
   * @param folder 上传到 Cloudinary 的文件夹
   * @returns 处理结果
   */
  async processMarkdownFile(
    filePath: string,
    uploadImages = false,
    generateNewFile = false,
    folder?: string,
  ): Promise<{
    extractedImages: string[];
    uploadedImages?: { original: string; cloudinary: string }[];
    newFilePath?: string;
    content?: string;
  }> {
    try {
      // 标准化路径
      const normalizedPath = path.normalize(filePath);

      // 提取图片 URL
      const extractedImages = this.extractImagesFromFile(normalizedPath);
      this.logger.log(
        `从 ${normalizedPath} 中提取到 ${extractedImages.length} 张图片`,
      );

      // 如果不需要上传图片或生成新文件，直接返回提取结果
      if (!uploadImages && !generateNewFile) {
        return { extractedImages };
      }

      // 读取文件内容
      const content = fs.readFileSync(normalizedPath, 'utf8');

      // 获取文件目录和文件名
      const dirPath = path.dirname(normalizedPath);
      const fileName = path.basename(normalizedPath, '.md');

      // 如果需要上传图片
      const uploadedImages: { original: string; cloudinary: string }[] = [];
      let updatedContent = content;

      if (uploadImages && extractedImages.length > 0) {
        // 检查是否有 images 文件夹
        const imagesDir = path.join(dirPath, 'images');
        if (!fs.existsSync(imagesDir)) {
          this.logger.warn(`images 文件夹不存在: ${imagesDir}`);
        } else {
          this.logger.log(`找到 images 文件夹: ${imagesDir}`);

          // 生成时间戳作为文件夹名前缀
          const timestamp = Date.now().toString();
          const cloudinaryFolder = folder
            ? `${folder}/${timestamp}`
            : timestamp;

          // 上传图片并记录结果
          for (const imageUrl of extractedImages) {
            try {
              // 如果是相对路径，尝试从 images 文件夹中找到图片
              if (!imageUrl.startsWith('http') && !path.isAbsolute(imageUrl)) {
                const imagePath = path.join(
                  imagesDir,
                  imageUrl.replace(/^images\//, ''),
                );

                if (fs.existsSync(imagePath)) {
                  this.logger.log(`上传图片: ${imagePath}`);

                  // 上传到 Cloudinary
                  const result = await this.uploadImage(imagePath, {
                    folder: cloudinaryFolder,
                    use_filename: true,
                    unique_filename: false,
                  });

                  // 记录原始 URL 和 Cloudinary URL
                  uploadedImages.push({
                    original: imageUrl,
                    cloudinary: result.secure_url,
                  });

                  // 如果需要生成新文件，则替换内容中的 URL
                  if (generateNewFile) {
                    updatedContent = updatedContent.replace(
                      new RegExp(
                        `!\\[.*?\\]\\(${imageUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\)`,
                        'g',
                      ),
                      `![](${result.secure_url})`,
                    );
                  }
                } else {
                  this.logger.warn(`图片文件不存在: ${imagePath}`);
                }
              } else {
                this.logger.log(`跳过非本地图片: ${imageUrl}`);
              }
            } catch (err) {
              this.logger.error(`上传图片失败: ${imageUrl}`, err);
            }
          }
        }
      }

      // 如果需要生成新文件
      let newFilePath: string | undefined;
      if (generateNewFile) {
        newFilePath = path.join(dirPath, `${fileName}-fix.md`);
        fs.writeFileSync(newFilePath, updatedContent, 'utf8');
        this.logger.log(`生成新文件: ${newFilePath}`);
      }

      return {
        extractedImages,
        uploadedImages: uploadImages ? uploadedImages : undefined,
        newFilePath,
        content: generateNewFile ? updatedContent : undefined,
      };
    } catch (error) {
      this.logger.error(`处理 Markdown 文件失败: ${filePath}`, error);
      throw error;
    }
  }
}
